import ImgCrop from 'antd-img-crop'
import {
  Upload, UploadProps, Modal
} from 'antd'
import type { UploadFile, RcFile } from 'antd/es/upload/interface'
import { useQuery } from '@apollo/client'
import { GET_OSS_INFO } from '@/graphql/oss'
import { useState } from 'react'

interface OSSDataType {
  dir: string
  expire: string
  host: string
  accessId: string
  policy: string
  signature: string
}

interface OSSUploadProps {
  value?: UploadFile[]
  label?: string
  maxCount?: number
  imgCropAspect?: number
  onChange?: (files: UploadFile[]) => void
}

const getBase64 = (file: RcFile): Promise<string> =>
  new Promise((resolve, reject) => {
  const reader = new FileReader()
  reader.readAsDataURL(file)
  reader.onload = () => resolve(reader.result as string)
  reader.onerror = error => reject(error)
})

const OSSImageUpload = ({
  label = '上传图片',
  maxCount = 1,
  imgCropAspect = 1 / 1,
  onChange = () => {},
}: OSSUploadProps) => {
  const { data, refetch } = useQuery<{ getOSSInfo: OSSDataType }>(GET_OSS_INFO)
  const [ fileList, setFileList ] = useState<UploadFile[]>([])
  const [previewOpen, setPreviewOpen] = useState(false)
  const [previewImage, setPreviewImage] = useState('')
  const [previewTitle, setPreviewTitle] = useState('')

  const OSSData = data?.getOSSInfo

  const getKey = (file: UploadFile) => {
    const suffix = file.name.slice(file.name.lastIndexOf('.'))
    const key = `${OSSData?.dir}${file.uid}${suffix}`
    const url = `${OSSData?.host}/${key}`
    return { key, url }
  }

  const handleChange: UploadProps['onChange'] = ({ fileList }) => {
    const files = fileList.map((f) => ({
      ...f,
      url: f.url || getKey(f).url,
    }))
    setFileList(files)
    onChange?.(files)
  }

  const getExtraData: UploadProps['data'] = (file) => ({
    key: getKey(file).key,
    OSSAccessKeyId: OSSData?.accessId,
    policy: OSSData?.policy,
    Signature: OSSData?.signature,
  })

  const beforeUpload: UploadProps['beforeUpload'] = async (file) => {
    if (!OSSData) return false

    const expire = Number(OSSData.expire) * 1000

    if (expire < Date.now()) {
      await refetch()
    }
    return file
  }

  const handleCancel = () => setPreviewOpen(false)

  const handlePreview = async (file: UploadFile) => {
    if (!file.preview) {
      file.preview = await getBase64(file.originFileObj as RcFile)
    }

    setPreviewImage(file.url || (file.preview as string))
    setPreviewOpen(true)
    setPreviewTitle(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1))
  }

  return (
    <>
      <ImgCrop aspect={imgCropAspect}>
        <Upload
          name="file"
          maxCount={maxCount}
          listType="picture-card"
          fileList={fileList}
          action={OSSData?.host}
          onChange={handleChange}
          onPreview={handlePreview}
          data={getExtraData}
          beforeUpload={beforeUpload}
        >
          {label}
        </Upload>
      </ImgCrop>
      <Modal open={previewOpen} title={previewTitle} footer={null} onCancel={handleCancel}>
        <img alt="图片" style={{ width: '100%' }} src={previewImage} />
      </Modal>
    </>
  )
}

export default OSSImageUpload
